/*
 * @Description: 优化版的吸附工具类
 * @Author: cgt
 * @Date: 2021-11-15 17:22:16
 * @LastEditors: cgt
 * @LastEditTime: 2022-02-17 12:13:20
 */
import { randomUUID } from '../../../mapUtils/RandomUtil';
import QuadtreeConcrete from './QuadTree/QuadtreeConcrete';
import Rectangle from './QuadTree/Rectangle';

class AdvancedSnapUtil {
  constructor(_map) {
    this.map = _map;
    /**
     * 全量待捕捉线段和面
     * 线 -> 点+线段
     * 面 -> 点+线段
     */
    this.fullCanBeSnapFeatures = [];
    this.fullCanBeSnapLayerId = 'fullCanBeSnapLayerId';
    // 可捕捉的图形
    this.totalFeatures = [];
    // 辅助样式
    this.snapBufferWidth = 15;
    this.snapBufferOpacity = 0;
    this.snapBufferCursorWidth = 15;
    // 每个节点最多容纳多少个点
    this.capacity = 20;
    this.snappedFeature = null;
    // 辅助要素，用于高亮捕捉到的要素和点
    this.assistFeatures = [];
    this.bestSnapInfo = [];

    this.initLayer();
    this.quadTree = null;
    this._updateQuadTree = this.updateQuadTree.bind(this);
    this._removeAssistFeatures = this.removeAssistFeatures.bind(this);
  }

  /**
   * 每个节点最多容纳多少个点，超过设置的节点，将创建子四叉树
   * @param {Number} capacity
   */
  setCapacity(capacity = 20) {
    this.capacity = capacity;
  }

  /**
   * 初始化捕捉图层
   */
  initLayer() {
    // 辅助捕捉数据源
    if(!this.map.getSource(this.fullCanBeSnapLayerId)) {
      this.map.addSource(this.fullCanBeSnapLayerId, {
        type: 'geojson',
        data: {
          type: 'FeatureCollection',
          features: []
        }
      });
    }
    // 全量待捕捉-点
    if(!this.map.getLayer(this.fullCanBeSnapLayerId + '_point')) {
      this.map.addLayer({
        id: this.fullCanBeSnapLayerId + '_point',
        type: 'symbol',
        source: this.fullCanBeSnapLayerId,
        filter: ['all', ['==', '$type', 'Point']],
        layout: {},
        paint: {
          'text-color': 'red'
        }
      });
    }
    // 全量待捕捉-线
    if(!this.map.getLayer(this.fullCanBeSnapLayerId + '_line')) {
      this.map.addLayer({
        id: this.fullCanBeSnapLayerId + '_line',
        type: 'line',
        source: this.fullCanBeSnapLayerId,
        filter: ['all', ['==', '$type', 'LineString']],
        layout: {},
        paint: {
          'line-opacity': 0
        }
      });
    }
    // 全量待捕捉-面
    if(!this.map.getLayer(this.fullCanBeSnapLayerId + '_polygon')) {
      this.map.addLayer({
        id: this.fullCanBeSnapLayerId + '_polygon',
        type: 'fill',
        source: this.fullCanBeSnapLayerId,
        filter: ['all', ['==', '$type', 'Polygon']],
        paint: {
          'fill-opacity': 0
        }
      });
    }
    // 辅助捕捉数据源
    if(!this.map.getSource('_snap_assist_source')) {
      this.map.addSource('_snap_assist_source', {
        type: 'geojson',
        data: {
          type: 'FeatureCollection',
          features: []
        }
      });
    }
    // 辅助捕捉数据源
    if(!this.map.getSource('_snap_assist_source')) {
      this.map.addSource('_snap_assist_source', {
        type: 'geojson',
        data: {
          type: 'FeatureCollection',
          features: []
        }
      });
    }
    // 辅助捕捉图层-圈
    if(!this.map.getLayer('_snap_vertex')) {
      this.map.addLayer({
        id: '_snap_vertex',
        type: 'circle',
        source: '_snap_assist_source',
        filter: ['all', ['==', '$type', 'Point']],
        paint: {
          'circle-color': 'transparent',
          'circle-radius': 5,
          'circle-stroke-width': 3,
          'circle-stroke-color': ['case', ['get', 'best'], 'red', 'transparent'] // 捕捉到的所有辅助点，最适合的/最终捕捉的点设置为红色
        }
      });
    }
    // 辅助捕捉图层-线
    if(!this.map.getLayer('_snap_line')) {
      this.map.addLayer({
        id: '_snap_line',
        type: 'line',
        source: '_snap_assist_source',
        filter: ['all', ['==', '$type', 'LineString']],
        layout: {
          'line-cap': 'round',
          'line-join': 'round'
        },
        paint: {
          'line-color': 'orange',
          'line-width': 3,
          'line-opacity': 1
        }
      });
    }
    // 辅助捕捉图层-捕捉面
    if(!this.map.getLayer('_snap_polygon')) {
      this.map.addLayer({
        id: '_snap_polygon',
        type: 'fill',
        source: '_snap_assist_source',
        filter: ['all', ['==', '$type', 'Polygon']],
        layout: {},
        paint: {
          'fill-color': 'orange',
          'fill-opacity': 0
        }
      });
    }
  }

  /**
   * 开始捕捉
   */
  start() {
    this.moveSnapLayersToTop();
    this.addMapChangeEvents();
  }

  /**
   * 停止捕捉
   */
  stop() {
    this.assistFeatures = [];
    this.removeMapChangeEvents();
    this.renderAssistFeatures();
  }

  addMapChangeEvents() {
    this.map.on('moveend', this._updateQuadTree);
    this.map.on('zoomend', this._updateQuadTree);
    this.map.on('mouseup', this._removeAssistFeatures);
  }

  removeMapChangeEvents() {
    this.map.off('moveend', this._updateQuadTree);
    this.map.off('zoomend', this._updateQuadTree);
    this.map.off('mouseup', this._removeAssistFeatures);
  }

  removeAssistFeatures() {
    this.assistFeatures = [];
    this.renderAssistFeatures();
  }
  /**
   * 捕捉方法
   * @param {Object} lngLat 当前位置坐标
   */
  snap(lngLat) {
    this.getSnapedFeature(lngLat);
    this.bestSnapInfo = this.getBestSnapInfo();
    if (this.bestSnapInfo.length > 0) {
      var bestSnapFeature = this.bestSnapInfo[0].feature;
      this.assistFeatures.forEach((f) => {
        f.feature.properties.best = f.feature.geometry.type === 'Point' && f.feature.properties.id === bestSnapFeature.properties.id;
      });
      this.renderAssistFeatures();
      var coord = turf.getCoord(this.bestSnapInfo[0].snapCoord);
      return {
        lng: coord[0],
        lat: coord[1],
        owner: this.bestSnapInfo[0].feature.properties?.id,
        ownerFeature: this.bestSnapInfo[0].feature
      };
    }
    this.renderAssistFeatures();
    return lngLat;
  }

  /**
   * 加载捕捉到的点
   */
  setSnapVertex(features) {
    this.map.getSource('_snap_assist_source').setData(turf.featureCollection(features));
  }

  getSnapedFeature(lngLat) {
    this.assistFeatures = [];
    if (!this.quadTree) {
      return;
    }
    // 点所在位置的屏幕坐标
    var centerPos = this.map.project([lngLat.lng, lngLat.lat]);
    var lbLngLat = this.map.unproject([centerPos.x - this.snapBufferWidth, centerPos.y + this.snapBufferWidth]);
    var trLngLat = this.map.unproject([centerPos.x + this.snapBufferWidth, centerPos.y - this.snapBufferWidth]);
    var searchArea = new Rectangle(
      lngLat.lng,
      lngLat.lat,
      (trLngLat.lng - lbLngLat.lng) / 2,
      (trLngLat.lat - lbLngLat.lat) / 2
    );
    var found = [];
    // 四叉树查找点
    this.quadTree.query(searchArea, found);
    // 查找所有的实际图形
    var truthFeatures = this.getTruthFeatures(found);
    for (var id in truthFeatures) {
      // 某个图形里面取个最近的点
      var distanceList = truthFeatures[id].points.map(({ x, y }) => {
        return this.getDistance([lngLat.lng, lngLat.lat], [x, y]);
      });
      var minDistance = Math.min(...distanceList);
      var idx = distanceList.indexOf(minDistance);
      // 最近点的坐标
      var neareastPointOnFeature = truthFeatures[id].points[idx];
      var point = turf.point([neareastPointOnFeature.x, neareastPointOnFeature.y]);
      point.properties.id = truthFeatures[id].feature.properties.id;
      this.appendAssistFeature(truthFeatures[id].feature, minDistance, false, point);
      this.appendAssistFeature(point, minDistance, true);
    }
    // found.forEach((item) => {
    //   var dis = turf.distance(turf.point([lngLat.lng, lngLat.lat]), turf.point([item.x, item.y]));
    //   this.appendAssistFeature(turf.point([item.x, item.y]), dis, true);
    // });
    // 辅助绘制多边形
    this.appendAssistFeature(turf.bboxPolygon([lbLngLat.lng, lbLngLat.lat, trLngLat.lng, trLngLat.lat]), 0, true);
  }

  getDistance([x1, y1], [x2, y2]) {
    return turf.distance([x1, y1], [x2, y2]);
    // return Math.sqrt(Math.pow(Math.abs(x1-x2), 2), Math.pow(Math.abs(y1-y2), 2));
  }
  getTruthFeatures(found) {
    var truthFeaturesMapping = {};
    found.forEach((item) => {
      var has = !!truthFeaturesMapping[item.feature.properties.id];
      if (!has) {
        truthFeaturesMapping[item.feature.properties.id] = {
          points: [],
          feature: item.feature
        };
      }
      truthFeaturesMapping[item.feature.properties.id].points.push({
        x: item.x,
        y: item.y
      });
    });
    return truthFeaturesMapping;
  }

  _getSnapedFeature(lngLat) {
    this.assistFeatures = [];
    // 缓冲半径
    var maxiumDistance = this.getMaxiumDistance();
    var i = 0;
    for (var l = this.totalFeatures.length; i < l; i++) {
      var feature = this.totalFeatures[i];
      var featureType = turf.getType(feature);
      var nearestPointOnLineDistance = null, nearestPointOnLine = null;

      if (featureType === 'Polygon') {
        // 多边形转成线，通过线来判断是否捕捉到边
        var polygonLine = turf.polygonToLine(feature);
        // 查找与鼠标点最近点的距离
        nearestPointOnLineDistance = turf.pointToLineDistance(turf.point([lngLat.lng, lngLat.lat]), polygonLine);
        if (nearestPointOnLineDistance <= maxiumDistance) {
          // 查找与鼠标点最近点
          nearestPointOnLine = turf.nearestPointOnLine(polygonLine, turf.point([lngLat.lng, lngLat.lat]));
          // 多边形
          this.appendAssistFeature(feature, nearestPointOnLineDistance, false, nearestPointOnLine);
          this.appendAssistFeature(polygonLine, 0, true);
          // 最近点
          this.appendAssistFeature(nearestPointOnLine, 0, true);
        }
      } else if (featureType === 'LineString') {
        // 查找与鼠标点最近点的距离
        nearestPointOnLineDistance = turf.pointToLineDistance(turf.point([lngLat.lng, lngLat.lat]), feature);
        if (nearestPointOnLineDistance <= maxiumDistance) {
          // 查找与鼠标点最近点
          nearestPointOnLine = turf.nearestPointOnLine(feature, turf.point([lngLat.lng, lngLat.lat]));
          // 线
          this.appendAssistFeature(feature, nearestPointOnLineDistance, false, nearestPointOnLine);
          // 最近点
          this.appendAssistFeature(nearestPointOnLine, 0, true);
        }
      } else if (featureType === 'Point') {
        // 查找与鼠标点最近点的距离
        var distance = turf.distance(turf.point([lngLat.lng, lngLat.lat]), feature);
        if (distance <= maxiumDistance) {
          // 点
          this.appendAssistFeature(feature, distance, false, feature);
          // 最近点
          this.appendAssistFeature(feature, 0, true);
        }
      }
    }
  }

  /**
   *
   * @param {Feature} feature 捕捉到的要素
   * @param {Number} distance 距离
   * @param {Boolean} isVertex 是否捕捉辅助点
   * @param {Feature} snapCoord 捕捉辅助点要素 isVertex为true时，snapCoord为空
   */
  appendAssistFeature(feature, distance, isVertex, snapCoord) {
    this.assistFeatures.push({
      feature,
      distance,
      isVertex,
      snapCoord
    });
  }

  renderAssistFeatures() {
    this.map.getSource('_snap_assist_source').setData(turf.featureCollection(this.assistFeatures.map((k) => k.feature)));
  }

  getBestSnapInfo() {
    var pointFeatures = this.assistFeatures.filter((k) => {
      return !k.isVertex && k.feature.geometry.type === 'Point';
    });
    if (pointFeatures.length > 0) {
      var nearestSnapFeature = this.getNearestSnapFeature(pointFeatures);
      return [nearestSnapFeature];
    }

    return this.assistFeatures
      .filter((k) => !k.isVertex)
      .sort((a, b) => {
        return a.distance - b.distance;
      });
  }

  getNearestSnapFeature(features) {
    features.sort((a, b) => {
      return a.distance - b.distance;
    });
    return features[0];
  }
  /**
   * 获取最大的可捕捉距离（捕捉缓冲区半径）
   * @returns
   */
  getMaxiumDistance() {
    var map = this.map;
    var dis = turf.distance(map.unproject([0, 0]).toArray(), map.unproject([this.snapBufferWidth, 0]).toArray());
    return dis;
  }

  /**
   * 加载待捕捉对象
   * @param {features} snapSource 捕捉对象
   * @param {*} filter 过滤条件
   */
  initSnapSource(snapSource) {
    var type = turf.getType(snapSource);
    if (Array.isArray(snapSource)) {
      this.totalFeatures = snapSource;
    } else if (type === 'FeatureCollection') {
      this.totalFeatures = snapSource.features;
    } else {
      console.error('snap source invalid');
    }
    this.map.getSource(this.fullCanBeSnapLayerId).setData(turf.featureCollection(this.totalFeatures));

    this.updateQuadTree();
  }
  updateQuadTree() {
    var points = this.getFullPoints();
    var center = this.map.getCenter().toArray();

    var boundArray = this.map.getBounds().toArray();

    var boundary = new Rectangle(
      center[0],
      center[1],
      (boundArray[1][0] - boundArray[0][0]) / 2,
      (boundArray[1][1] - boundArray[0][1]) / 2
    );
    window.allPoints = [];
    this.quadTree = new QuadtreeConcrete(boundary, this.capacity, points);
  }

  _pointInsideView([lng, lat], [[x1, y1], [x2, y2]]) {
    return lng >= x1 && lng <= x2 && lat >= y1 && lat <= y2;
  }
  _lineInsideView(segment, [[x1, y1], [x2, y2]]) {
    return window.turf.booleanIntersects(segment, window.turf.bboxPolygon([x1, y1, x2, y2]));
  }
  /**
   * 等分线
   * @param {Feature} line 需要等分的线
   * @param {Object} coordGap 每像素的坐标大小
   */
  _splitLine(line, { lngGap, latGap }) {
    var segments = window.turf.lineSegment(line);
    var box = this.map.getBounds().toArray();
    var newLineCoords = [];
    // if(segments.features.length>5000){
    //   return window.turf.getCoords(line);
    // }
    segments.features.forEach((segment) => {
      var [[fromLng, fromLat], [toLng, toLat]] = segment.geometry.coordinates;

      // 判断当前线段是否在视野范围内，如果不在视野范围内，不做线段分割
      if (this._lineInsideView(segment, box)) {
        // if(this._pointInsideView([fromLng, fromLat], box)
        //   || this._pointInsideView([toLng, toLat], box)){

        // 经度和纬度的分隔份数
        var lngDivideCount = Math.abs((fromLng - toLng) / lngGap);
        var latDivideCount = Math.abs((fromLat - toLat) / latGap);
        // 取最大的切割份数
        var divideCount = Math.max(lngDivideCount, latDivideCount);
        // 重新计算经度和纬度的切割间距
        var truthLngGap = Math.abs((fromLng - toLng) / divideCount),
          truthLatGap = Math.abs((fromLat - toLat) / divideCount);
        var calcLngGap = truthLngGap,
          calcLatGap = truthLatGap;

        // var check = null;
        if (fromLng > toLng) {
          // right -> left
          calcLngGap = -truthLngGap;
        } else {
          // left -> right
          calcLngGap = truthLngGap;
        }
        if (fromLat > toLat) {
          // top -> bottom
          calcLatGap = -truthLatGap;
        } else {
          calcLatGap = truthLatGap;
        }

        var dividedSegmentCoordinates = [];
        var preisInView = false;
        for (var i = 0; i < divideCount; i++) {
          if (i === 0) {
            dividedSegmentCoordinates.push([fromLng, fromLat]);
          } else {
            var _lng = fromLng + i * calcLngGap,
              _lat = fromLat + i * calcLatGap;
            var currentisInView = this._pointInsideView([_lng, _lat], box);
            if (preisInView || currentisInView) {
              dividedSegmentCoordinates.push([fromLng + i * calcLngGap, fromLat + i * calcLatGap]);
            }
            preisInView = currentisInView;
          }
        }

        if (
          dividedSegmentCoordinates.length > 0 &&
          dividedSegmentCoordinates[dividedSegmentCoordinates.length - 1][0] !== toLng
        ) {
          dividedSegmentCoordinates.push([toLng, toLat]);
        }
        newLineCoords.push(...dividedSegmentCoordinates);
      } else {
        newLineCoords.push(
          ...[
            [fromLng, fromLat],
            [toLng, toLat]
          ]
        );
      }
    });
    return newLineCoords;
  }

  _getPerSegmentCoord() {
    // 切割像素精度
    var splitCapacity = 1;
    var lnglat1 = this.map.unproject([0, 0]).toArray();
    var lnglat2 = this.map.unproject([splitCapacity, splitCapacity]).toArray();
    var lngGap = lnglat2[0] - lnglat1[0];
    var latGap = lnglat1[1] - lnglat2[1];
    return {
      lngGap: Math.abs(lngGap),
      latGap: Math.abs(latGap)
    };
  }

  appendFeatureId(feature) {
    if (feature.properties) {
      if (feature.properties.id === undefined || feature.properties.id === null) {
        feature.properties.id = randomUUID();
      }
    } else {
      feature.properties = {
        id: randomUUID()
      };
    }
  }
  getFullPoints() {
    var visibleFeatures = this.totalFeatures;
    // var canvas = this.map.getCanvas();
    // var visibleFeatures = this.map.queryRenderedFeatures([[0, 0], [canvas.clientWidth, canvas.clientHeight]], { layers:[
    //   this.fullCanBeSnapLayerId+"_point",
    //   this.fullCanBeSnapLayerId+"_line",
    //   this.fullCanBeSnapLayerId+"_polygon",
    // ] });
    var perSegmentCoord = this._getPerSegmentCoord();

    var points = [];
    visibleFeatures.forEach((feature) => {
      // append properties id
      this.appendFeatureId(feature);
      var type = window.turf.getType(feature);
      if (type === 'Point') {
        var coord = window.turf.getCoord(feature);
        points.push({
          x: coord[0],
          y: coord[1],
          feature: feature
        });
      } else if (type === 'LineString') {
        var coords = this._splitLine(feature, perSegmentCoord);
        // var coords = window.turf.getCoords(feature);
        // var coords = window.turf.getCoords(newLine);
        points.push(
          ...coords.map(([x, y]) => {
            return {
              x,
              y,
              feature
            };
          })
        );
      } else if (type === 'Polygon') {
        var polygonLine = window.turf.polygonToLine(feature);

        var coords2 = this._splitLine(polygonLine, perSegmentCoord);
        // var coords2 = window.turf.getCoords(newPolygonLine);
        points.push(
          ...coords2.map(([x, y]) => {
            return {
              x,
              y,
              feature
            };
          })
        );
      }
    });
    return points;
  }

  /**
   * 将绘制的图层移到地图最上层
   */
  moveSnapLayersToTop() {
    let glDrawLayers = ['_snap_vertex', '_snap_line', '_snap_polygon'];
    // let glDrawLayers = ["_snap_vertex"];
    glDrawLayers.forEach((layer) => {
      this.map.moveLayer(layer);
    });
  }
}
export default AdvancedSnapUtil;
